import { Component, ComponentInterface, Element, Host, Method, forceUpdate, h } from '@stencil/core';
import interact from 'interactjs';

/**
 * 分割面板
 *
 * @export
 * @class IbzSplitView
 */
@Component({
    tag: 'ibz-split-view',
    styleUrl: 'ibz-split-view.scss',
})
export class IBzSplitView implements ComponentInterface {
    /**
     * 当前组件Dom实例
     *
     * @type {HTMLIbzSplitViewElement}
     * @memberof IBzSplitView
     */
    @Element()
    el: HTMLIbzSplitViewElement;

    /**
     * 分割面板容器
     *
     * @private
     * @type {HTMLDivElement}
     * @memberof IBzSplitView
     */
    private splitContainer!: HTMLDivElement;

    /**
     * 所有子面板
     *
     * @private
     * @type {HTMLIbzSplitViewPanelElement[]}
     * @memberof IBzSplitView
     */
    private panels: HTMLIbzSplitViewPanelElement[] = [];

    /**
     * 最小高度
     *
     * @private
     * @memberof IBzSplitView
     */
    private minHeight = 0;

    /**
     * 旧子项计数
     *
     * @private
     * @memberof IBzSplitView
     */
    private oldChildCount = 0;

    /**
     * 正在拖拽中面板
     *
     * @private
     * @type {HTMLIbzSplitViewPanelElement}
     * @memberof IBzSplitView
     */
    private draggingPanel?: HTMLIbzSplitViewPanelElement;

    componentWillRender(): void {
        if (this.splitContainer && this.splitContainer.childElementCount !== this.oldChildCount) {
            this.calcPanels();
        }
    }

    componentDidLoad(): void {
        this.calcPanels();
        this.calcChildUIState();
        this.tick();
    }

    connectedCallback(): void {
        window.addEventListener('resize', this.windowResize.bind(this));
    }

    disconnectedCallback(): void {
        window.removeEventListener('resize', this.windowResize.bind(this));
    }

    /**
     * 窗口大小变更
     *
     * @memberof IBzSplitView
     */
    async windowResize() {
        let oldContainerHeight = 0;
        const containerHeight = this.el.offsetHeight;
        const arr: number[] = [];
        this.panels.forEach(item => {
            oldContainerHeight += item.height;
        });
        this.panels.forEach(item => {
            arr.push(item.height / oldContainerHeight);
        });
        arr.forEach((num, i) => {
            const item = this.panels[i];
            const height = containerHeight * num;
            item.setHeight(height);
            if (i === 0) {
                item.top = 0;
            } else {
                const beforePanel = this.panels[i - 1];
                item.top = beforePanel.top + beforePanel.height;
            }
        });
    }

    /**
     * 计算最小高度
     *
     * @memberof IBzSplitView
     */
    @Method()
    async calcMinHeight() {
        this.minHeight = 0;
        this.panels.forEach(item => (this.minHeight += item.minHeight));
        this.tick();
    }

    /**
     * 计算子
     *
     * @memberof IBzSplitView
     */
    calcPanels(): void {
        const { children, childElementCount } = this.splitContainer;
        this.oldChildCount = childElementCount;
        for (let i = 0; i < childElementCount; i++) {
            const item: HTMLIbzSplitViewPanelElement = children[i] as any;
            if (item.tagName === 'IBZ-SPLIT-VIEW-PANEL') {
                item.container = this.el;
                this.panels.push(item);
            }
        }
        this.panels.forEach((item, i, arr) => {
            if (i > 0) {
                item.beforePanel = arr[i - 1];
            }
            if (i < arr.length - 1) {
                item.afterPanel = arr[i + 1];
            }
            item.panels = arr;
        });
        this.calcMinHeight();
    }

    /**
     * 重新计算UI状态
     *
     * @memberof IBzSplitView
     */
    calcChildUIState(): void {
        const splitCount = this.panels.length;
        const panelDefaultHeight = this.el.offsetHeight / splitCount;
        this.panels.forEach((item, i) => {
            item.top = i * panelDefaultHeight;
            item.setHeight(panelDefaultHeight);
        });
    }

    /**
     * 初始化拖拽
     *
     * @param {HTMLIbzSplitSashElement} item
     * @param {number} i
     * @return {*}
     * @memberof IBzSplitView
     */
    initDrag(item: HTMLIbzSplitSashElement, i: number) {
        const selfEL: any = item;
        // 设置下标
        selfEL.index = i;
        if (selfEL.isDragInit) {
            return;
        }
        // 设置为已设置拖拽状态
        selfEL.isDragInit = true;
        const panel = this.panels[i];
        panel.index = i;
        panel.dragTrigger = item;
        interact(selfEL).draggable({
            // 锁定起始轴
            startAxis: 'y',
            // 锁定可拖拽轴
            lockAxis: 'y',
            // 设置拖拽生效区域
            modifiers: [
                // 保持在父对象内部
                interact.modifiers.restrictEdges({ outer: this.el }),
            ],
            inertia: true,
            cursorChecker: () => {
                return 'ns-resize';
            },
            onmove: (e: any) => {
                this.calcPanelLayout(e);
            },
            onstart: () => {
                this.draggingPanel = panel;
                this.panels.forEach(self => self.draggingStart());
            },
            onend: () => {
                this.draggingPanel = undefined;
                this.panels.forEach(self => self.draggingEnd());
            },
        });
    }

    /**
     * 计算面板项布局
     *
     * @param {*} e
     * @memberof IBzSplitView
     */
    calcPanelLayout(e: any): void {
        this.draggingPanel.draggingMove(this.draggingPanel.top + e.delta.y);
        this.tick();
    }

    render() {
        return (
            <Host class='ibz-split-view' style={{ 'min-height': this.minHeight + 'px' }}>
                <div class='sash-container'>
                    {this.panels.map((item, i) => {
                        if (i === 0) {
                            return;
                        }
                        return (
                            <ibz-split-sash
                                key={i}
                                ref={ref => this.initDrag(ref, i)}
                                visible={item.visible}
                                top={item.top}
                            />
                        );
                    })}
                </div>
                <div class='split-view-container' ref={ref => (this.splitContainer = ref)}>
                    <slot />
                </div>
            </Host>
        );
    }

    /**
     * 强制更新
     *
     * @memberof IBzSplitView
     */
    @Method()
    async tick() {
        forceUpdate(this);
    }
}
